package pkg.andru.net;

import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.apache.http.HttpStatus;

import android.os.Build;
import android.util.Log;

/**
 * 
 * @author idiottiger
 * 
 */
public class BetterHttpURLConnection {

    static final String TAG = "BetterHttpURLConnection";

    /**
     * fix the http url connection bug below 2.2
     */
    static {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) {
            System.setProperty("http.keepAlive", "false");
        }
    }

    static final int DEFAULT_CONNECT_TIMEOUT = 5000, DEFAULT_READ_TIMEOUT = 15000;

    Map<String, String> mRequestHeaderMap = new HashMap<String, String>();

    int mConnectTimeout, mReadTimeout;
    private HttpURLConnection mUrlConnection;
    private URL mRequestURL;

    public BetterHttpURLConnection() {
        HttpURLConnection.setFollowRedirects(true);
        mConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
        mReadTimeout = DEFAULT_READ_TIMEOUT;
    }

    /**
     * set request header
     * 
     * @param header
     * @param value
     */
    public void setRequestHeader(String header, String value) {
        mRequestHeaderMap.put(header, value);
    }

    public void clearRequestHeader() {
        mRequestHeaderMap.clear();
    }

    /**
     * set connect timeout
     * 
     * @param timeout
     */
    public void setConnectTimeout(int timeout) {
        mConnectTimeout = timeout;
    }

    /**
     * set socket read timeout
     * 
     * @param timeout
     */
    public void setReadTimeout(int timeout) {
        mReadTimeout = timeout;
    }

    /**
     * do http connection, support all method in {@link HttpRequestMethod} enum
     * 
     * @param method
     * @param url
     * @param callback
     */
    public void doConnect(HttpRequestMethod method, String url, HttpURLConnectionCallback callback) {
        HttpURLConnection httpConnection = null;
        try {
            mRequestURL = new URL(url);
            httpConnection = (HttpURLConnection) mRequestURL.openConnection();
            mUrlConnection = httpConnection;

            // set connection timeout
            httpConnection.setConnectTimeout(mConnectTimeout);
            // set read timeout
            httpConnection.setReadTimeout(mReadTimeout);
            // set the request method
            httpConnection.setRequestMethod(method.getMethodName());
            // set the do input and output
            boolean doIn = method.getDoInput(), doOut = method.getDoOutput();
            httpConnection.setDoInput(doIn);
            httpConnection.setDoOutput(doOut);

            // add the request header
            if (!mRequestHeaderMap.isEmpty()) {
                Set<String> keys = mRequestHeaderMap.keySet();
                for (String key : keys) {
                    httpConnection.setRequestProperty(key, mRequestHeaderMap.get(key));
                }
            }

            // preconnection callback
            if (callback != null) {
                callback.onPreConnection(httpConnection, method, url);
            }

            // begin connect
            httpConnection.connect();

            // if can out, will write first and do get input
            if (doOut && callback != null) {
                callback.onWriteStream(httpConnection.getOutputStream());
            }

            if (doIn && callback != null) {
                callback.onGetStream(httpConnection.getResponseCode(), httpConnection);
            }

        } catch (MalformedURLException e) {
            throw new IllegalArgumentException(String.format("URL:[%s] isnot a valid URL", url));
        } catch (Exception e) {
            Log.e(TAG, "network error");
            if (httpConnection != null && callback != null) {
                HttpRequestException ex = new HttpRequestException(e != null ? e.getMessage() : "error", httpConnection.getErrorStream());
                callback.onError(ex);
            }
        } finally {
            // always close the connection
            if (httpConnection != null) {
                httpConnection.disconnect();
                httpConnection = null;
                mUrlConnection = null;
                mRequestURL = null;
            }
        }
    }

    /**
     * simple do get method
     * 
     * @param url
     * @return
     * @throws HttpRequestException
     */
    public InputStream doGet(String url) throws HttpRequestException {
        HttpURLConnection httpConnection = null;
        try {
            mRequestURL = new URL(url);
            httpConnection = (HttpURLConnection) mRequestURL.openConnection();
            mUrlConnection = httpConnection;

            // set connection timeout
            httpConnection.setConnectTimeout(mConnectTimeout);
            // set read timeout
            httpConnection.setReadTimeout(mReadTimeout);
            // set the request method
            httpConnection.setRequestMethod(HttpRequestMethod.GET.getMethodName());
            // set the do input and output
            httpConnection.setDoInput(true);
            httpConnection.setDoOutput(false);

            // add the request header
            if (!mRequestHeaderMap.isEmpty()) {
                Set<String> keys = mRequestHeaderMap.keySet();
                for (String key : keys) {
                    httpConnection.setRequestProperty(key, mRequestHeaderMap.get(key));
                }
            }
            // begin connect
            httpConnection.connect();

            if (httpConnection.getResponseCode() == HttpStatus.SC_OK) {
                return httpConnection.getInputStream();
            }
        } catch (Exception e) {
            Log.e(TAG, "network error");
            if (httpConnection != null) {
                HttpRequestException ex = new HttpRequestException(e != null ? e.getMessage() : "error", httpConnection.getErrorStream());
                throw ex;
            }
        }
        return null;
    }

    public void close() {
        if (mUrlConnection != null) {
            mUrlConnection.disconnect();
            mUrlConnection = null;
        }
    }

    public boolean isNetworkNeedSignOn() {
        String host = mRequestURL.getHost();
        String newhost = mUrlConnection.getURL().getHost();

        Log.i(TAG, String.format("HOST COMPARE['%s' - '%s']", host, newhost));
        return !host.equals(newhost);
    }

}
